Vue + Koa2 移动电商项目学习笔记 ( 一 )
开发环境搭建
建立文件夹
新建 SmileVue 文件夹
使用 vue-cli 生成项目目录
- 使用 npm -v 检测 npm 版本,尽量使用 5.X 以上版本
- 全局安装 vue-cli, npm install vue-cli -g
- 在终端中输入 vue init webpack 来初始化项目, 项目名需要小写
测试环境是否安装成功
- 使用 npm run dev 进行测试环境的打开
- 在浏览器中输入 localhost:8080 进行测试
引入 Vant 组件库
安装 Vant
npm i vant -S
引入 Vant 的方法一 (不推荐)
安装好 Vant 后,可以使用以前常用的方法进行引入—这是一种全局引入的方法
1
2
3
4
5// main.js
import Vant from 'vant'
import 'vant/lib/vant-css/index.css'
Vue.use(vant)不推荐这种方法的原因是在最后打包发布的时候会增加包的大小, Vue 的 SPA 首屏打开时间本来就有些慢
引入 Vant 推荐方法
Vant 是支持 babel-plugin-import 引入的,可以按需引入组件模块,并且不用管理我们的样式,是现在 Vue 项目组件库的主流引入方法。
安装 babel-plugin-import
npm i babel-plugin-import -D
或者 npm install babel-plugin-import –save-dev
在 .babelrc 中配置 plugins
1
2
3
4
5"plugins": [
"transform-vue-jsx",
"transform-runtime",
["import",{"libraryName":"vant","style":true}]
]按需使用 Vant 组件
在设置好 .babelrc 后,就可以按需引入 Vant 框架了。比如引入一个 Button 组件
1
2
3
4
5
6// main.js
import { Button } from 'vant'
Vue.use(Button)
// 有了这段代码之后,我们就可以在需要的组件页面中假如 Button 了
<van-button type="primary">主要按钮</van-button>
移动端屏幕适配基础
常见移动 Web 布局适配方法
- 固定高度,固定百分比:过时的方法
- Media Query (媒体查询):现在比较主流的适配方案
flex 布局: 主流的布局方式,在项目中尽量采用 flex + rem 的方式进行布局和完成移动端的适配。
rem 单位介绍
适配原理:将 px 替换成 rem,动态修改 html 的 font-size 适配。它可以很好的根据根元素的字体大小来进行变化,从而达到各种屏幕基本一致的效果体验。
JS 控制适配屏幕
1
2
3
4
5
6
7
8
9
10// 得到手机屏幕的宽度
let htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;
// 得到 html 的 DOM 元素
let htmlDom = document.getElementsByTagName('html')[0];
// 设置根元素字体大小
htmlDom.style.fontSize = htmlWidth/20 + 'px';
// 当页面很大的时候,我们的移动适配方案会呈现很大的字体,所以加一个判断,解决页面字体过大的问题
// 当页面宽度大于 750px 时, 把页面的宽度设置为 750px
if ( htmlWidth> 750 ) { htmlWidth=750 }
首页布局和路由设置
首页路由的配置:
打开路由配置页面:src/router/index.js,先删除 vue-cli 自动生成的 HelloWorld.Vue 的配置,然后加入新的路由配置
1 | import Vue from 'vue' |
建立首页组件
删除 HelloWorld.vue 文件,建立 ShoppingMall.vue 文件。代码如下:
1 | <template> |
Vant 布局
使用 Vant 的布局需要先进行按需引入,直接在 main.js 里引入 Row 和 Col 组件
import { Button, Row, Col } from ‘vant’
Vue.use(Button).use(Row).use(Col)
Vant 采用 24 格布局法,我们控制这 24 等分的比例就可以实现布局
1 | <van-row> |
首页搜索区域的布局
搜索条的布局
利用 van-row 和 van-col 快速布局一个搜索条的 HTML 骨架
1
2
3
4
5
6
7
8
9
10
11
12
13<div class="search-bar">
<van-row>
<van-col span="3">icon</van-col>
<van-col span="16">serach input</van-col>
<van-col span="5">button</van-col>
</van-row>
</div>
.search-bar{
height: 2.2rem;
background-color: #e5017d;
line-height:2.2rem;
}下载 icon 图标
在 src/assets/ 目录下新建 images 文件夹,以后所有的项目图片都放在这里
引入图片
把图片用 require 引进到页面中
1
2
3
4
5
6
7
8
9
10// 通过绑定属性的方法插入图片
<van-col span="3"><img :src="locationIcon" width="100%" /></van-col>
export default {
data() {
return {
locationIcon: require('../../assets/images/location.png')
}
},
}写 input 和 button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50<template>
<div>
<!--search bar layout-->
<div class="search-bar">
<van-row gutter="5">
<van-col span="3"><img :src="locationIcon" width="80%" class="location-icon" /></van-col>
<van-col span="16">
<input type="text" class="search-input"/>
</van-col>
<van-col span="5"><van-button size="mini">查找</van-button></van-col>
</van-row>
</div>
</div>
</template>
<script>
export default {
data() {
return {
locationIcon: require('../../assets/images/location.png')
}
},
}
</script>
<style scoped>
.search-bar{
height: 2.2rem;
background-color: #e5017d;
line-height:2.2rem;
}
.search-input{
width:100%;
height: 1.3rem;
border-top:0px;
border-left:0px;
border-right:0px;
border-bottom: 1px solid 1px !important ;
background-color: #e5017d;
color:#fff;
}
.location-icon{
padding-top: .2rem;
padding-left: .3rem;
}
</style>
首页轮播图的制作
按需加载 Swipe 组件
在 /src/main.js 下按需引入 swipe
import { Swipe, SwipeItem } from ‘vant’;
Vue.use(Swipe).use(SwipeItem);
现在的 main.js 样式如下:
import { Button, Row, Col ,Search , Swipe , SwipeItem } from ‘vant’
Vue.use(Button).use(Row).use(Col).use(Search).use(Swipe).use(SwipeItem)
下载图片
开始制作轮播图
在 js 部分写入一个 data 参数 bannerPicArray,把图片地址放入到里边
1 | // /src/components/pages/ShoppingMall.vue |
利用 Vant 实现图片轮播的懒加载
import { Button, Row, Col ,Search , Swipe , SwipeItem , Lazyload } from ‘vant’
Vue.use(Button).use(Row).use(Col).use(Search).use(Swipe).use(SwipeItem).use(Lazyload)
// 修改 template 区域,加入
v-lazy="banner.imageUrl"
就可以图片的懒加了
easyMock 和 Axios 的使用
Mock 数据准备
进入 easy-Mock
axios 的引入
直接使用 npm install 进行安装
npm install –save axios
安装之后在要使用的页面组件中进行引入
import axios from ‘axios’
然后在 created 的声明周期里取得数据
1 | created(){ |
如果能取得数据后,说明已经 Mock 成功了,那接下来就用这些数据进行布局
Mock 数据的使用 flex 布局
首页商品分类栏的布局
使用 flex 布局,是因为 van-row 是 24 格布局,5 个元素是不好分的,所以使用 flex 布局。
在 js 代码 created 的 axios then 方法里写入下面代码 ( 提取 Mock 数据 )
1 | created(){ |
编写 HTML 代码
1 | <div class="type-bar"> |
CSS 样式
1 | .type-bar{ |
广告 Banner 的布局
1 | // 先在 created 里获取数据,然后进行 HTML 骨架编写,最后进行 CSS 样式的调整 |
改造 swipe 组件
1 | // 前面已经用静态数据模拟了轮播效果,现在有了Mock数据,完全可以用Mack数据代替。 |
商品推荐 vue-awesome-swiper
先来简单的布局
1 | // 我们先把基本的布局做好,在src/components/pages/ShoppingMall.vue,里编写如下html和CSS代码,这里只是简单的布局。 |
安装 vue-awesome-swiper
npm install vue-awesome-swiper –save
引入 vue-awesome-swiper 的两种方式
全局引入
1
2
3
4
5
6// 可以直接使用全局引入
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'
// require styles
import 'swiper/dist/css/swiper.css'
Vue.use(VueAwesomeSwiper, /* { default global options } */)以组件的形式引入
1
2
3
4
5
6
7
8
9// 以组件形式引入 这种方式是在需要的页面以component 的形式引入,好处就是依赖性不强。
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
components: {
swiper,
swiperSlide
}
}
获取推荐商品数据
在javascript
部分的data里加入recommendGoods:[]
属性,然后在created
生命周期里获得.
this.recommendGoods = response.data.data.recommend //推荐商品
编写 swiper 的 html
1 | <!--swiper--> |
vue-awesome-swiper 详解1
vue-awesome-swiper
组件在开发中是经常使用的,它可以作轮播图,可以作滚动。
一个最简单的轮播图
这里作一个单独的组件,这样不会污染项目中的文件,这个只是一个最简单默认的swiper,在components目录下新建一个文件夹swiper
,然后新建一个swiperDefault.vue
文件。写入如下代码:
1 | <template> |
代码写好后,在shoppingMall.vue
文件里进行引入使用。
import swiperDefault from ‘../swiper/swiperDefault’
然后注册组件,
components:{swiper,swiperSlide,swiperDefault},
注册好后,直接在template
里使用就可以了.
添加分页器
1 | // 我们现在data里进行配置,代码如下: |
最后是在swiper
标签里加入 :options="swiperOption"
。就实现了有分页期的效果。
整体代码如下:
1 | <template> |
竖屏切换效果
在配置项里直接配置direction
就可以了,配置竖屏代码如下
1 | swiperOption:{ |
整体代码如下:
1 | <template> |
vue-awesome-swiper 详解2
区域滚动效果
1 | <template> |
重点看一下 options 的加入属性:
- direction: ‘vertical’ 设置竖排显示
- slidesPerView: ‘auto’ 设置同屏显示的数量, 默认为 1, 这里使用 auto 是随意的意思。
- freeMode: true 默认为 false, 普通模式: slide 滑动时只滑动一格,并自动贴合 wrapper,设置为 true 则变为 free 模式, slide 会根据惯性滑动可能不止一格且不会贴合。
- mousewheel: true 开启鼠标滚轮控制 Swiper 切换。 可是只鼠标选项, 或 true 使用默认值。
让分页器可以自由选择
在实际工作当中分页器都是可以自由选择的,只要配置一下 Options 的 clickable 数据就可以了。注意这个属性要配置在 pagination 下面,才能起作用。
1 | pagination: { |
无线循环滚动
还有一个需求是无限循环滚动,不要到底了就要往回滚动,这个只要在 options 里加一个 loop: true 就可以实现了。
1 | data() { |
首页楼层区域布局
不规则的布局
第一步: 先获取楼层一的数据
在 data 里注册一个 floor1 的数组变量 floor1:[], 在 axios 里得到数据。
1
2
3
4this.floor1 = response.data.data.floor1 //楼层1数据
this.floor1_0 =this.floor1[0]
this.floor1_1 =this.floor1[1]
this.floor1_2 =this.floor1[2]第二步: 编写 HTML 代码
在编写 HTML 的时候需要注意结构层次,原则就是先统一规划大体,然后在调整局部
1
2
3
4
5
6
7
8
9
10<!--floor one area-->
<div class="floor">
<div class="floor-anomaly">
<div class="floor-one"><img :src="floor1_0.image" width="100%" /></div>
<div>
<div class="floor-two"><img :src="floor1_1.image" width="100%" /></div>
<div><img :src="floor1_2.image" width="100%" /></div>
</div>
</div>
</div>第三步: 编写 CSS 样式
主要使用了 flex 布局和 box-sizing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19.floor-anomaly{
display: flex;
flex-direction:row;
background-color: #fff;
border-bottom:1px solid #ddd;
}
.floor-anomaly div{
width:10rem;
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.floor-one{
border-right:1px solid #ddd;
}
.floor-two{
border-bottom:1px solid #ddd;
}
规则部分的布局
第一步: 布局 HTML
1
2
3
4
5<div class="floor-rule">
<div v-for="(item ,index) in floor1.slice(3)" :key="index">
<img :src="item.image" width="100%"/>
</div>
</div>第二步: 编写 CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16.floor-rule{
display: flex;
flex-direction: row;
flex-wrap:wrap;
background-color: #fff;
}
.floor-rule div{
-webkit-box-sizing: border-box;
box-sizing: border-box;
width:10rem;
border-bottom:1px solid #ddd;
}
.floor-rule div:nth-child(odd){
border-right: 1px solid #ddd;
}
楼层标题的布局
自定义样式
1 | /* 完整代码 */ |
楼层组件的封装和 watch 的使用
为了代码复用和少写一些代码,将楼层这部分分封装成一个传递参数的组件,并使用 watch 来监听参数的变化,达到正确渲染的目的。
编写组件
在 src/components/ 下新建一个 component 的文件夹, 进入文件夹, 新建 floorComponent.vue 文件, 用来编写楼层组件
在编写的时候,先从页面中把相对应的 html 模板和 CSS 代码拷进来,然后再进行改造。 全部组件代码如下:
1 | <template> |
由于组件中的数据是从远程拿来的,所以刚开始数据是空的,所以组件渲染不出来。需要加入 watch 属性来监听传递过来值的变化,当变化时, 再给 1, 2, 3副图片进行赋值操作。
最后根据赋值来改造 template 的 HTML 结构
引入组件
在 ShoppingMall.vue 中引入组件, 首先使用 import 进行引入
import floorComponent from ‘../component/floorComponent’
在 components 属性里注册组件
components: {floorComponent}
完成上面两步就可以直接在 template 里用标签的形式使用了
使用绑定属性的形式传入需要的值, 按照这种方法, 就可以直接引入其他 3 个楼层了。
删除页面中的无用代码
有了组件后,页面中的很多代码都变得无用了,我们需要作一下处理
楼层组件的完善
楼层组件标题区域制作
首先在 floorComponent.vue 组件的 template 区域加入 HTML 代码
然后编写 css 样式
1 | .floor-title{ |
其次加入 props 属性的编写
props: [ ‘floorData’, ‘floorTitle’ ],
这样我们就完成了楼层组件区域的制作,并且可以在说那个的时候传递过来一个标题了。
给头部区域传值
先在 ShoppingMall.vue 的 Data 里声明一个叫 floorName 的值。 然后在 axios 里进行赋值。
this.floorName = response.data.data.floorName // 楼层名称
最后修改一下 template 里的写法就可以使用了
完成其他两个楼层的代码编写
先在 data 里进行声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16data() {
return {
swiperOption: {
slidesPerView:3,
},
locationIcon: require('../../assets/images/location.png'),
bannerPicArray:[], // 轮播图片
category:[], //商品类别标签栏
adBanner:'', //广告图片
recommendGoods:[], //推荐商品
floor1:[], //楼层1的数据
floor2:[], //楼层1的数据
floor3:[], //楼层1的数据
floorName:{} //楼层名称
}
},在 axios 里获得数据
1
2
3this.floor1 = response.data.data.floor1 //楼层1数据
this.floor2 = response.data.data.floor2 //楼层2数据
this.floor3 = response.data.data.floor3 //楼层3数据在模板 template 里使用组件
1
2
3<floorComponent :floorData="floor1" :floorTitle="floorName.floor1"></floorComponent>
<floorComponent :floorData="floor2" :floorTitle="floorName.floor2"></floorComponent>
<floorComponent :floorData="floor3" :floorTitle="floorName.floor3"></floorComponent>
Filter 在实战中的使用
编写过滤器通用方法
因为过滤器都是需要在很多组件中进行使用,所以要编写一个比较通用的方法。先在 src 文件夹下建立一个 filter 文件夹, 然后在 filter 文件夹下建立一个 moneyFilter.js 文件。
这时候就可以编写格式化钱的方法, 我们这里使用了 toFixed() 方法。
1 | export function toMoney (money) { |
引入 Filter
import { toMoney } from “@/filter/moneyFilter.js”
这里的 @ 代表的是 src 目录的意思,这个是 webpack 的配置,我们可以在 /build/webpack.base.conf.js 里找到这个配置项。
编写 Vue 里的 filter 属性
vue 是支持 filter 属性的,之前只是方法,现在要在 Vue 文件里编写属性.
1 | filters: { |
在 template 中使用 filter
1 | 直接在使用价格的地方使用 {{ item.price | moneyFilter }} 就可以了 |
优化 filter 通用方法
1 | // 上边的通用方法做的并不完善,在这里需要优化一下 |
首页热卖模块的 Van-list 组件的使用
html + css 部分的编写
1 | // html 代码,这些代码是写在 ShoppingMall.vue 文件中的 |
Vant 列表 (List) 组件的使用
引入 List 组件: 在 /src/main.js 文件中引入 List 组件
import { List } from “vant”
Vue.use( List )
构造数据: 在 data 里声明 hotGoods
data(){ hotGoods: [] } // 热卖商品
在 axios 里获得数据
this.hotGoods = response.data.data.hotGoods // 热卖商品
加入 List 组件,并使用 van-row 布局
1
2
3
4
5
6
7
8
9
10
11
12
13<!--Hot Area-->
<div class="hot-area">
<div class="hot-title">热卖商品</div>
<div class="hot-goods">
<van-list>
<van-row gutter="20">
<van-col span="12" v-for="( item, index) in hotGoods" :key="index">
<div>{{item.name}}</div>
</van-col>
</van-row>
</van-list>
</div>
</div>商品显示组件的编写
新建 /src/component/goodsInfoComponent.vue 文件,用来制作商品组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<template>
<div class="goods-info">
<div class="goods-image">
<img v-lazy="goodsImage" width="90%" />
</div>
<div class="goods-name">{{goodsName}}</div>
<div class="goods-price">¥{{goodsPrice | moneyFilter }}</div>
</div>
</template>
<script>
import {toMoney} from '@/filter/moneyFilter.js'
export default {
props:['goodsImage','goodsName','goodsPrice'],
filters:{
moneyFilter(money){
return toMoney(money)
}
},
}
</script>
<style scoped>
.goods-name{
padding: 0 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space:nowrap;
}
</style>在 ShoppingMall.vue 里引入,并且在 components 里声明一下
import goodsInfo from ‘../component/goodsInfoComponent’
在模板中使用组件
1
2
3
4
5<van-row gutter="20">
<van-col span="12" v-for="(item,index) in hotGoods" :key="index">
<goods-info :goodsImage="item.image" :goodsName="item.name" :goodsPrice="item.price"></goods-info>
</van-col>
</van-row>
编写后台服务接口配置文件
在开发中我们直接把数据接口写到了 axios 中,这样写如果地址改变或者接口改变,我们需要进入业务逻辑代码进行修改,维护起来会非常麻烦,现在我们把项目中用到的接口都单独拿出来,做一个接口配置文件 serviceAPI.config.js
编写接口配置文件
在项目 src 目录下建立 serviceAPI.config.js,代码如下:
1 | const BASEURL = "https://www.easy-mock.com/mock/5ae2eeb23fbbf24d8cd7f0b6/SmileVue/" |
编写好后,就可以直接在要使用的文件中用 import 的形式引入。
import url from ‘@.serviceAPI.config.js’
引入后就可以直接使用了
1 | axios({ |